package com.fir.gateway.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.fir.gateway.config.GlobalConfig;
import com.fir.gateway.config.exception.CustomException;
import com.fir.gateway.config.result.AjaxStatus;
import com.fir.gateway.dto.ConnectDTO;
import com.fir.gateway.dto.RsaKeyDTO;
import com.fir.gateway.service.IAuthService;
import com.fir.gateway.utils.AESUtils;
import com.fir.gateway.utils.MD5Utils;
import com.fir.gateway.utils.RSAUtils;
import com.fir.gateway.utils.SaltedHashUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Map;


/**
 * @author fir
 * @date 2023/4/23 17:03
 */
@Slf4j
@Service
public class AuthServiceImpl implements IAuthService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;


    /**
     * 网关参数配置
     */
    @Resource
    private GlobalConfig globalConfig;


    /**
     * 获取公钥
     *
     * @return 公钥
     */
    @Override
    public String getPublicKey() {

        // 生成本此次连接的公钥与私钥对存储，将公钥发送到前端作为加密通信签名的方式
        Map<String, String> map = RSAUtils.generateKey();

        String publicKey = map.get(RSAUtils.PUBLIC_KEY);
        String privateKey = map.get(RSAUtils.PRIVATE_KEY);

        // 将公钥取md5后，作为key存入redis中
        String publicKeyMd5 = MD5Utils.generateMd5ForString(publicKey);
        RsaKeyDTO rsaKeyDTO = new RsaKeyDTO();
        rsaKeyDTO.setPublicKey(publicKey);
        rsaKeyDTO.setPrivateKey(privateKey);

        Object obj = JSONObject.toJSON(rsaKeyDTO);
        redisTemplate.opsForValue().set(publicKeyMd5, obj,
                globalConfig.getConnectExpirationTime(), globalConfig.getConnectExpirationTimeUNIT());

        return publicKey;
    }


    /**
     * 获取通信加密信息
     *
     * @param publicKeyMd5       RSA公钥
     * @param clientPublicKey 客户端公钥
     * @return 加密信息
     */
    @Override
    public ConnectDTO info(String publicKeyMd5, String clientPublicKey) {
        ConnectDTO connectDTO;
        // 将公钥取md5后，作为key存入redis中
        JSONObject jsonObject = (JSONObject) redisTemplate.opsForValue().get(publicKeyMd5);
        if (jsonObject != null) {
            RsaKeyDTO rsaKeyDTO = jsonObject.toJavaObject(RsaKeyDTO.class);
            String publicKey = rsaKeyDTO.getPublicKey();
            String privateKey = rsaKeyDTO.getPrivateKey();
            clientPublicKey = RSAUtils.decryptSection(clientPublicKey, privateKey);

            String secretKey = AESUtils.generateKeyAES();
            String sessionId = generateSessionId();
            String salt = SaltedHashUtils.generateSalt();
            connectDTO = ConnectDTO.builder()
                    .secretKey(secretKey)
                    .sessionId(sessionId)
                    .salt(salt)
                    .privateKey(privateKey)
                    .clientPublicKey(clientPublicKey)
                    .build();
            redisTemplate.opsForValue()
                    .set(sessionId, JSONObject.toJSON(connectDTO),
                            globalConfig.getConnectExpirationTime(), globalConfig.getConnectExpirationTimeUNIT());
            connectDTO = ConnectDTO.builder()
                    .secretKey(secretKey)
                    .sessionId(sessionId)
                    .salt(salt)
                    .publicKey(publicKey)
                    .clientPublicKey(clientPublicKey)
                    .build();
            return connectDTO;
        } else {
            throw new CustomException(AjaxStatus.FAILED_COMMUNICATION);
        }
    }

    private static final SecureRandom RANDOM = new SecureRandom();

    public static String generateSessionId() {
        byte[] bytes = new byte[32];
        RANDOM.nextBytes(bytes);
        return Base64.getEncoder().encodeToString(bytes);
    }


}
